/**
 * Copyright 2010 Tristan Slominski
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.opensource.org/licenses/mit-license.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mindash.handlebars.client.pages.impl;

import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.mindash.handlebars.client.adapter.Adapter;
import com.mindash.handlebars.client.events.RefreshViewEvent;
import com.mindash.handlebars.client.pages.Page;
import com.mindash.handlebars.client.pages.utils.Redirect;
import com.mindash.handlebars.client.places.AtPlace;
import com.mindash.handlebars.client.places.GoToPlace;
import com.mindash.handlebars.client.places.handlers.AtPlaceGenericHandler;
import com.mindash.handlebars.client.presenters.impl.DefaultParentPresenterImpl;
import com.mindash.handlebars.client.services.EventBus;
import com.mindash.handlebars.client.services.PlaceService;
import com.mindash.handlebars.client.views.View;
import com.mindash.handlebars.client.wrappers.RootPanelWrapper;

/**
 * @author Tristan Slominski
 * 
 */
public abstract class DefaultPageImpl<V extends View> extends
    DefaultParentPresenterImpl<V> implements Page {

  /* injected dependencies */
  @Inject
  private PlaceService placeService;
  @Inject
  protected Redirect redirect;
  @Inject
  protected RootPanelWrapper rootPanel;

  /* variables */
  private HandlerRegistration atPlaceRegistration;
  @SuppressWarnings("unused")
  private GwtEvent.Type<?> pageType;
  private HandlerRegistration refreshViewRegistration;

  @Inject
  public DefaultPageImpl(EventBus eventBus, Provider<V> viewProvider) {
    super(eventBus, viewProvider);
    // set the page type
    setPageType(getPageType());
    // register on event bus
    setAtPlaceRegistration(registerAtPlaceHandler());
    // set adapter to display
    setAdapter(Adapter.displayAdapter);
  }

  /**
   * <p>
   * Should look like:
   * </p>
   * <p>
   * {@code return AtPlaceProvider.<pick a type>;}
   * </p>
   * 
   * @return the type of place this page is associated with (one of
   * <code>AtPlaceProvider.type</code> types)
   */
  public abstract GwtEvent.Type<?> getPageType();

  /**
   * <p>
   * Registers this page as a listener for the specific AtPlace event on the
   * event bus. Should look like:
   * </p>
   * <p>
   * {@code return eventBus.addHandler(AtPlaceProvider.<pick a type>, this);}
   * </p>
   * 
   * @return the handler registration
   */
  public abstract HandlerRegistration registerAtPlaceHandler();

  public Widget createView() {
    return makeView().createAndBind();
  }

  @Override
  public void displayPage() {
    // clear what we got displayed
    getRootPanel().clearPage();
    // add the created page
    getRootPanel().addPage(createView());
  }

  @Override
  public void forceOnAtPlaceExecution() {
    // not applicable to how Pages work
  }

  public RootPanelWrapper getRootPanel() {
    return rootPanel;
  }

  /**
   * Registers this page to handle refresh view events on the event bus.
   * Override this method but be sure to include a call to {@code
   * super.onAtPlace(place)} to make sure the page correctly registers the
   * refresh event with the event bus.
   * 
   * @param place the place the page is at.
   */
  public <T extends AtPlaceGenericHandler> void onAtPlace(AtPlace<T> place) {
    super.onAtPlace(place);
    // register with events on the event bus
    if (getRefreshViewRegistration() == null) {
      setRefreshViewRegistration(eventBus.addHandler(new RefreshViewEvent(null)
          .getAssociatedType(), this));
    }
  }
  
  @Override
  public <T extends AtPlaceGenericHandler> void onRefreshViewEvent(
      AtPlace<T> place) {
    // make sure to check that the refresh applies to this page:
    if (place.getAssociatedType().equals(getPageType())
        && placeService.atPlaceEquals(place)) {
      refreshPage();
    } else {
      // someone else is being refreshed, ie, we aren't being shown, remove self
      // from refresh listeners
      getRefreshViewRegistration().removeHandler();
      setRefreshViewRegistration(null);
    }
  }

  @Override
  public Timer redirect(GoToPlace<?> goTo, int secondsDelay) {
    return redirect.doRedirect(goTo, secondsDelay);
  }

  @Override
  public void refreshPage() {
    // clear what we got displayed
    getRootPanel().clearPage();
    // add the created page
    getRootPanel().addPage(refreshView());
  }
  
  /**
   * Refresh the view using already existing views in the presenters.
   * 
   * @return the view as widget to be added to root panel
   */
  public Widget refreshView() {
    return makeView().createAndBind();
  }

  /* getters and setters */
  
  public void setPageType(GwtEvent.Type<?> pageType) {
    this.pageType = pageType;
  }

  public void setRefreshViewRegistration(
      HandlerRegistration refreshViewRegistration) {
    this.refreshViewRegistration = refreshViewRegistration;
  }

  public HandlerRegistration getRefreshViewRegistration() {
    return refreshViewRegistration;
  }

  public void setAtPlaceRegistration(HandlerRegistration atPlaceRegistration) {
    this.atPlaceRegistration = atPlaceRegistration;
  }

  public HandlerRegistration getAtPlaceRegistration() {
    return atPlaceRegistration;
  }

}
